package edu.zcmu.seckill.service.impl;

import com.alibaba.fastjson.JSON;
import com.auth0.jwt.interfaces.Claim;
import com.auth0.jwt.interfaces.DecodedJWT;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import edu.zcmu.seckill.entity.SeckillOrder;
import edu.zcmu.seckill.entity.SeckillProduct;
import edu.zcmu.seckill.enumeration.GlobalEnum;
import edu.zcmu.seckill.exception.MyException;
import edu.zcmu.seckill.mapper.SeckillOrderMapper;
import edu.zcmu.seckill.response.R;
import edu.zcmu.seckill.response.ResultCode;
import edu.zcmu.seckill.rocket.producer.OrderProducer;
import edu.zcmu.seckill.rocket.to.OrderTo;
import edu.zcmu.seckill.service.SeckillOrderService;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import edu.zcmu.seckill.service.VerifyService;
import edu.zcmu.seckill.utils.JwtUtil;
import edu.zcmu.seckill.utils.Md5Utils;
import edu.zcmu.seckill.utils.SnowFlake;
import edu.zcmu.seckill.vo.GenerateOrderVo;
import edu.zcmu.seckill.vo.PayVo;
import edu.zcmu.seckill.vo.SeckillOrderInfoVo;
import edu.zcmu.seckill.vo.SeckillOrderVo;
import lombok.extern.slf4j.Slf4j;
import org.redisson.api.RSemaphore;
import org.redisson.api.RedissonClient;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.ValueOperations;
import org.springframework.stereotype.Service;
import org.springframework.util.StringUtils;

import javax.servlet.http.HttpServletRequest;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.concurrent.TimeUnit;

/**
 * <p>
 *  服务实现类
 * </p>
 *
 * @author 唐诗
 * @since 2022-02-22
 */
@Slf4j
@Service
public class SeckillOrderServiceImpl extends ServiceImpl<SeckillOrderMapper, SeckillOrder> implements SeckillOrderService {


    @Autowired
    JwtUtil jwtConfig;

    @Autowired
    RedisTemplate redisTemplate;

    @Autowired
    RedissonClient redissonClient;

    @Autowired
    OrderProducer orderProducer;

    @Autowired
    VerifyService verifyService;
    @Override
    public String generateOrderTest(String id,HttpServletRequest request) {
        //获取用户token
        DecodedJWT tokenInfo = JwtUtil.getTokenInfo(request.getHeader(jwtConfig.getHeader()));
        String uid = tokenInfo.getClaim("id").asString();  //获得token中携带的用户id

        if(StringUtils.isEmpty(id)){
            throw new MyException(ResultCode.ERROR,"系统异常");
        }
        //判断用户重复抢购
        String s = (String) redisTemplate.opsForValue().get(GlobalEnum.USER_SECKILL_PRODUCT_IS.getCode() + uid + ":" + id);
        if (!StringUtils.isEmpty(s)){
            throw new MyException(ResultCode.ERROR,"用户重复抢购");
        }
        //秒杀地址比较


        RSemaphore semaphore = redissonClient
                .getSemaphore(GlobalEnum.GET_SECKILL_PRODUCT_COUNT_REDIS.getCode() + id);
        String orderId="";
        try {
            //拿库存
            boolean flag = semaphore.tryAcquire(1, 100, TimeUnit.MILLISECONDS);
            if (flag){
                SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
                orderId = new SnowFlake(2, 3).nextId();
                Boolean aBoolean = redisTemplate.opsForValue().setIfAbsent(GlobalEnum.USER_SECKILL_PRODUCT_IS.getCode() + uid + ":" + id, orderId + "_" + sdf.format(new Date()));
                if (!aBoolean){
                    semaphore.release();
                    throw  new MyException(ResultCode.ERROR,"活动火爆,请稍后再试");
                }
                log.info("秒杀成功：订单号：{}",orderId);
                redisTemplate.opsForValue().set(GlobalEnum.USER_SECKILL_PRODUCT_IS_EXPIRATION.getCode() + uid + ":" + id+":"+orderId,"1",15,TimeUnit.MINUTES);
                //发送消息队列
                OrderTo orderTo = new OrderTo();
                orderTo.setId(orderId);
                orderTo.setProductId(id);
                orderTo.setUserId(uid);
                orderProducer.sendMessage(orderTo);
            }else {
                throw new MyException(ResultCode.ERROR,"产品已经售空");
            }
        } catch (InterruptedException e) {
            throw new MyException(ResultCode.ERROR,"活动火爆,请稍后再试");
        }
        return orderId;

    }
    /**
     * 获取秒杀地址
     * @param id
     * @param code
     * @param request
     * @return
     */
    @Override
    public String getSeckillPath(Integer id, String code, HttpServletRequest request) {

        String s = (String) redisTemplate.opsForValue().get(GlobalEnum.GET_SECKILL_PRODUCT_REDIS.getCode() + id);//获取当前用户秒杀产品的信息
        SeckillProduct product = JSON.parseObject(s, SeckillProduct.class);
        if (product==null){
            throw new MyException(ResultCode.ERROR,"没有该产品");
        }
        //获取用户token
        String token = request.getHeader(jwtConfig.getHeader());
        DecodedJWT tokenInfo = JwtUtil.getTokenInfo(token);
        String uid = tokenInfo.getClaim("id").asString();  //获得token中携带的用户id
        //判断验证码
        if (code==null||"".equals(code)){
          throw new MyException(ResultCode.ERROR,"验证码错误");
        }
        String  verifyCode= (String) redisTemplate.opsForValue().get(GlobalEnum.VERIFY_CODE.getCode() + uid);
        if (!code.equals(verifyCode)){
            throw new MyException(ResultCode.ERROR,"验证码错误");
        }
        String policy = tokenInfo.getClaim("policy").asString();  //获得token中携带的用户筛选条件
        String age = (String) tokenInfo.getClaim("age").asString();
        String policySP= (String) redisTemplate.opsForValue().get(GlobalEnum.GET_SECKILL_PRODUCT_POLICY_REDIS.getCode() + id);
        Boolean aBoolean = this.judgeUser(policy, policySP, age);
        //判断用户是否满足条件购买
        if (!aBoolean){
            throw new MyException(ResultCode.ERROR,"不符合该产品购买条件");
        }
        Date nowDate = new Date(System.currentTimeMillis());//当前时间
        if (!nowDate.before(product.getStartTime()) && !nowDate.after(product.getEndTime())) {
            //秒杀中
            String path = Md5Utils.encrypt(uid + nowDate);
            log.info("秒杀地址：path={}",path);

            redisTemplate.opsForValue()
                    .set(GlobalEnum.SECKILL_PRODUCT_PATH.getCode() + uid + ":" + id, path, 15, TimeUnit.SECONDS);
            return path;
        } else {
            //产品过期或者未到时间
            throw new MyException(ResultCode.ERROR,"产品过期或者未到秒杀时间");
        }
    }

    /**
     * 生成订单
     * @param generateOrderVo
     * @param request
     */
    @Override
    public String generateOrder(GenerateOrderVo generateOrderVo,HttpServletRequest request) {
        //获取用户token
        DecodedJWT tokenInfo = JwtUtil.getTokenInfo(request.getHeader(jwtConfig.getHeader()));
        String uid = tokenInfo.getClaim("id").asString();  //获得token中携带的用户id

        if (generateOrderVo==null){
            throw new MyException(ResultCode.ERROR,"系统异常");
        }
        if(StringUtils.isEmpty(generateOrderVo.getId())
                ||StringUtils.isEmpty(generateOrderVo.getPath())){
            throw new MyException(ResultCode.ERROR,"系统异常");
        }
        //判断用户重复抢购
        String s = (String) redisTemplate.opsForValue().get(GlobalEnum.USER_SECKILL_PRODUCT_IS.getCode() + uid + ":" + generateOrderVo.getId());
        if (!StringUtils.isEmpty(s)){
            throw new MyException(ResultCode.ERROR,"用户重复抢购");
        }
        //秒杀地址比较
        if (!checkPath(uid,generateOrderVo)){
            throw new MyException(ResultCode.ERROR,"系统异常");
        }

        RSemaphore semaphore = redissonClient
                .getSemaphore(GlobalEnum.GET_SECKILL_PRODUCT_COUNT_REDIS.getCode() + generateOrderVo.getId());
        String orderId="";
        try {
            //拿库存
            boolean flag = semaphore.tryAcquire(1, 100, TimeUnit.MILLISECONDS);
            if (flag){
                SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
                orderId = new SnowFlake(2, 3).nextId();
                log.info("秒杀成功：订单号：{}",orderId);
                Boolean aBoolean = redisTemplate.opsForValue().setIfAbsent(GlobalEnum.USER_SECKILL_PRODUCT_IS.getCode() + uid + ":" + generateOrderVo.getId(), orderId + "_" + sdf.format(new Date()),15,TimeUnit.MINUTES);
                if (!aBoolean){
                    throw  new MyException(ResultCode.ERROR,"活动火爆,请稍后再试");
                }
                redisTemplate.opsForValue().set(GlobalEnum.USER_SECKILL_PRODUCT_IS_EXPIRATION.getCode() + uid + ":" + generateOrderVo.getId()+":"+orderId,"1",15,TimeUnit.MINUTES);
                //发送消息队列
                OrderTo orderTo = new OrderTo();
                orderTo.setId(orderId);
                orderTo.setProductId(generateOrderVo.getId());
                orderTo.setUserId(uid);
                orderProducer.sendMessage(orderTo);
            }else {
                throw new MyException(ResultCode.ERROR,"产品已经售空");
            }
        } catch (InterruptedException e) {
            throw new MyException(ResultCode.ERROR,"活动火爆,请稍后再试");
        }
        return orderId;

    }

    /**
     * 添加秒杀订单
     * @param order
     * @return
     */
    @Override
    public int createSeckillOrder(SeckillOrder order) {
        return  baseMapper.insert(order);

    }

    @Override
    public SeckillOrderVo querySeckillProduct(SeckillOrderInfoVo vo, HttpServletRequest request) {
        //获取用户token
        DecodedJWT tokenInfo = JwtUtil.getTokenInfo(request.getHeader(jwtConfig.getHeader()));
        String uid = tokenInfo.getClaim("id").asString();  //获得token中携带的用户id
        if (vo==null){
            throw new MyException(ResultCode.ERROR,"系统异常");
        }
        if (StringUtils.isEmpty(uid)){
            throw new MyException(ResultCode.ERROR,"用户信息异常");
        }
        String orderId = vo.getOrderId();
        Integer productId = vo.getProductId();
        ValueOperations valueOperations = redisTemplate.opsForValue();
        String o = (String) valueOperations.get(GlobalEnum.USER_SECKILL_PRODUCT_IS.getCode() + uid + ":" + productId);
        if(StringUtils.isEmpty(o)){
            throw new MyException(ResultCode.ERROR,"订单已经过期");
        }
        String[] s = o.split("_");
        if (!orderId.equals(s[0])){
            throw new MyException(ResultCode.ERROR,"订单已经过期");
        }
        //获取秒杀产品信息
        String p = (String) valueOperations.get(GlobalEnum.GET_SECKILL_PRODUCT_REDIS.getCode() + productId.toString());
        SeckillProduct product = JSON.parseObject(p, SeckillProduct.class);
        //过期剩余时间
        Long expire = valueOperations.getOperations().getExpire(GlobalEnum.USER_SECKILL_PRODUCT_IS_EXPIRATION.getCode() + uid + ":" + productId+":"+s[0]);
        SeckillOrderVo seckillOrderVo = SeckillOrderVo.createSeckillOrderVo(product, s[1], expire,s[0]);
        return seckillOrderVo;
    }

    /**
     * 更新订单状态
     */
    @Override
    public void updateSeckillOrder(SeckillOrder seckillOrder) {
        baseMapper.update(seckillOrder,new QueryWrapper<SeckillOrder>().eq("order_id",seckillOrder.getOrderId())
                .eq("seckill_id",seckillOrder.getSeckillId())
                .eq("user_id",seckillOrder.getUserId()));
    }

    @Override
    public SeckillOrder querySeckillOrder(String orderId, String productId, String userId) {
        QueryWrapper<SeckillOrder> seckillOrderQueryWrapper = new QueryWrapper<>();
        SeckillOrder seckillOrder = baseMapper.selectOne(seckillOrderQueryWrapper.eq("order_id", orderId).eq("seckill_id", productId).eq("user_id", userId));
        return seckillOrder;
    }

    @Override
    public Page<SeckillOrder> getUserOrderByUid(Page<SeckillOrder> page,String uid) {
        Page<SeckillOrder> user_id = baseMapper.selectPage(page, new QueryWrapper<SeckillOrder>().eq("user_id", uid));
        return user_id;
    }

    @Override
    public int delUserOrder(PayVo payVo, String uid) {
        int delete = baseMapper.delete(new QueryWrapper<SeckillOrder>()
                .eq("order_id", payVo.getOrderId())
                .eq("seckill_id", payVo.getProductId())
                .eq("user_id", uid));

        return delete;
    }

    /**
     * 对比秒杀路径
     * @param uid
     * @param generateOrderVo
     * @return
     */
    public Boolean checkPath(String uid,GenerateOrderVo generateOrderVo){
        String path = generateOrderVo.getPath();
        String id = generateOrderVo.getId();
        String pathInRedis = (String) redisTemplate.opsForValue().get(GlobalEnum.SECKILL_PRODUCT_PATH.getCode() + uid + ":" + id);
        return path.equals(pathInRedis);
    }

    /**
     * 购买条件对比筛选
     * @param userJudge 用户的初筛
     * @param seckillProductJudge 商品条件
     * @param userAge 用户年龄
     * @return
     */
    Boolean judgeUser(String userJudge,String seckillProductJudge,String userAge){
        String[] s = seckillProductJudge.split("_");
        char[] chars = s[0].toCharArray();
        char[] chars1 = userJudge.toCharArray();
        for (int i=0;i<chars.length;i++){
            if (chars[i]=='1'&&chars1[i]!='1'){
             return false;
            }
        }
        if (Integer.valueOf(userAge)>=Integer.valueOf(s[1])){
            return true;
        }
            return false;
    }

}
